'''
Function:
    Implementation of NeteaseMusicClient utils
Author:
    Zhenchao Jin
WeChat Official Account (微信公众号):
    Charles的皮卡丘
'''
import os
import json
import base64
import urllib
import codecs
import urllib.parse
from hashlib import md5
from Crypto.Cipher import AES
from cryptography.hazmat.primitives import padding
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes


'''EapiCryptoUtils'''
class EapiCryptoUtils(object):
    '''hexdigest'''
    @staticmethod
    def hexdigest(data: bytes):
        return "".join([hex(d)[2:].zfill(2) for d in data])
    '''hashdigest'''
    @staticmethod
    def hashdigest(text: str):
        return md5(text.encode("utf-8")).digest()
    '''hashhexdigest'''
    @staticmethod
    def hashhexdigest(text: str):
        return EapiCryptoUtils.hexdigest(EapiCryptoUtils.hashdigest(text))
    '''encryptparams'''
    @staticmethod
    def encryptparams(url: str, payload: dict, aes_key: bytes = b"e82ckenh8dichen8"):
        url_path = urllib.parse.urlparse(url).path.replace("/eapi/", "/api/")
        digest = EapiCryptoUtils.hashhexdigest(f"nobody{url_path}use{json.dumps(payload)}md5forencrypt")
        params = f"{url_path}-36cd479b6b5-{json.dumps(payload)}-36cd479b6b5-{digest}"
        padder = padding.PKCS7(algorithms.AES(aes_key).block_size).padder()
        padded_data = padder.update(params.encode()) + padder.finalize()
        cipher = Cipher(algorithms.AES(aes_key), modes.ECB())
        encryptor = cipher.encryptor()
        enc = encryptor.update(padded_data) + encryptor.finalize()
        return EapiCryptoUtils.hexdigest(enc)


'''WeapiCryptoUtils'''
class WeapiCryptoUtils(object):
    '''createsecretkey'''
    @staticmethod
    def createsecretkey(size: int):
        return (''.join(map(lambda xx: (hex(ord(xx))[2:]), str(os.urandom(size)))))[0: 16]
    '''aesencrypt'''
    @staticmethod
    def aesencrypt(string: str, sec_key: str):
        pad = 16 - len(string) % 16
        if isinstance(string, bytes): string = string.decode('utf-8')
        string = string + str(pad * chr(pad))
        sec_key = sec_key.encode('utf-8')
        encryptor = AES.new(sec_key, 2, b'0102030405060708')
        string = string.encode('utf-8')
        ciphertext = encryptor.encrypt(string)
        ciphertext = base64.b64encode(ciphertext)
        return ciphertext
    '''rsaencrypt'''
    @staticmethod
    def rsaencrypt(string: str, pub_key: str = '010001', modulus: str = '00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7'):
        string = string[::-1]
        rs = int(codecs.encode(string.encode('utf-8'), 'hex_codec'), 16) ** int(pub_key, 16) % int(modulus, 16)
        return format(rs, 'x').zfill(256)
    '''encryptparams'''
    @staticmethod
    def encryptparams(params: dict):
        string = json.dumps(params)
        sec_key = WeapiCryptoUtils.createsecretkey(16)
        enc_string = WeapiCryptoUtils.aesencrypt(string=WeapiCryptoUtils.aesencrypt(string=string, sec_key='0CoJUm6Qyw8W8jud'), sec_key=sec_key)
        enc_sec_key = WeapiCryptoUtils.rsaencrypt(string=sec_key)
        post_data = {'params': enc_string, 'encSecKey': enc_sec_key}
        return post_data